/*
**  jstest-gtk - A graphical joystick tester
**  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmail.com>
**
**  This program is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef HEADER_JSTEST_GTK_JOYSTICK_HPP
#define HEADER_JSTEST_GTK_JOYSTICK_HPP

#include <linux/joystick.h>
#include "joystick_description.hpp"

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "g29_msg/msg/g29_msg.hpp"

// 声明前向引用，减少编译依赖
class XMLReader;
class XMLWriter;

/**
 * Joystick_pub 类封装了与游戏手柄相关的操作，包括读取手柄事件、校准、
 * 设置轴和按钮的映射等。它还提供了加载和保存手柄配置的功能。
 */
class Joystick_pub : public rclcpp::Node
{
public:
    /**
     * 校准数据结构，用于存储手柄轴的校准信息。
     */
    struct CalibrationData
    {
        bool calibrate; // 是否进行校准
        bool invert;    // 是否反转轴的方向
        int center_min; // 中心位置的最小值
        int center_max; // 中心位置的最大值
        int range_min;  // 轴的最小值
        int range_max;  // 轴的最大值
    };

private:

    int fd; // 文件描述符，用于与设备通信
    std::string filename;  // 设备文件名
    std::string orig_name; // 原始设备名称
    std::string name;      // 手柄名称
    int axis_count;        // 轴的数量
    int button_count;      // 按钮的数量
    int heartbeat;
    std::vector<int> axis_state;                        // 轴的状态
    std::vector<int> button_state;                      // 按钮的状态
    std::vector<CalibrationData> orig_calibration_data; // 原始校准数据

    rclcpp::Publisher<g29_msg::msg::G29Msg>::SharedPtr g29_publisher;
    rclcpp::TimerBase::SharedPtr g29_pub_timer;

public:
    /**
     * 构造函数，初始化Joystick_pub对象。
     * @param filename 设备文件名。
     */
    Joystick_pub(const std::string &filename, const std::string &node_name = "g29_publisher_node");

    /**
     * 析构函数，释放资源。
     */
    ~Joystick_pub();

    /**
     * 获取文件描述符。
     * @return 文件描述符。
     */
    int get_fd() const { return fd; }

    /**
     * 更新手柄状态。
     */
    void update();

    /**
     * 接收USB数据，并用ros2消息发布
     */
    bool on_in_publish();

    void publish();

    /**
     * 获取设备文件名。
     * @return 设备文件名。
     */
    std::string get_filename() const { return filename; }

    /**
     * 获取手柄名称。
     * @return 手柄名称。
     */
    std::string get_name() const { return name; }

    /**
     * 获取轴的数量。
     * @return 轴的数量。
     */
    int get_axis_count() const { return axis_count; }

    /**
     * 获取按钮的数量。
     * @return 按钮的数量。
     */
    int get_button_count() const { return button_count; }

    // 轴移动的处理函数
    void axis_update(int axis, int value);

    // 按钮移动的处理函数
    void button_update(int button, int value);

    /**
     * 获取指定轴的状态。
     * @param id 轴的标识符。
     * @return 轴的状态。
     */
    int get_axis_state(int id);

    /**
     * 获取指定按钮的状态。
     * @param id 按钮的标识符。
     * @return 按钮的状态。
     */
    int get_button_state(int id);

    /**
     * 获取系统中所有手柄的描述信息。
     * @return 手柄描述信息列表。
     */
    static std::vector<JoystickDescription> get_joysticks();

    /**
     * 获取校准数据。
     * @return 校准数据列表。
     */
    std::vector<CalibrationData> get_calibration();

    /**
     * 设置校准数据。
     * @param data 校准数据列表。
     */
    void set_calibration(const std::vector<CalibrationData> &data);

    /**
     * 重置校准数据到默认值。
     */
    void reset_calibration();

    /**
     * 清除所有校准数据，注意这将意味着使用原始USB输入值，
     * 而不是缩放后的值。
     */
    void clear_calibration();

    /**
     * 获取按钮映射。
     * @return 按钮映射列表。
     */
    std::vector<int> get_button_mapping();

    /**
     * 获取轴映射。
     * @return 轴映射列表。
     */
    std::vector<int> get_axis_mapping();

    /**
     * 设置按钮映射。
     * @param mapping 按钮映射列表。
     */
    void set_button_mapping(const std::vector<int> &mapping);

    /**
     * 设置轴映射。
     * @param mapping 轴映射列表。
     */
    void set_axis_mapping(const std::vector<int> &mapping);

    /**
     * 在重新映射轴后修正校准数据。
     * @param mapping_old 旧的轴映射。
     * @param mapping_new 新的轴映射。
     */
    void correct_calibration(const std::vector<int> &mapping_old, const std::vector<int> &mapping_new);

    /**
     * 将手柄配置写入XML。
     * @param out XML写入器。
     */
    void write(XMLWriter &out);

    /**
     * 从XML加载手柄配置。
     * @param reader XML读取器。
     */
    void load(const XMLReader &reader);

    /**
     * 获取此手柄设备基于的evdev设备文件名。这个调用只是一个猜测，
     * 不保证是相同的设备文件，但对于我们的用途来说已经足够。
     * @return evdev设备文件名。
     */
    std::string get_evdev() const;

private:
    /**
     * 禁止拷贝构造函数，防止对象拷贝。
     */
    Joystick_pub(const Joystick_pub &);

    /**
     * 禁止赋值操作符，防止对象赋值。
     */
    Joystick_pub &operator=(const Joystick_pub &);
};

#endif

/* EOF */
